کانال بله, جهت پشتیبانی و اطلاع رسانی کانال بله, جهت پشتیبانی و اطلاع رسانی
عضویت

آموزش Test unit در اندروید استودیو

مبحث حاضر به شما آموزش می دهد چگونه می توانید برای اپلیکیشن های اندرویدی خود instrumentation & unit test طراحی نموده و از عملکرد صحیح بخش های مختلف پروژه خود اطمینان حاصل نمایید. سپس برای شما شرح می دهد چگونه این تست ها را در محیط کاری Android Studio و با استفاده از سیستم کامپایل Gradle اجرا نمایید.

برای یادگیری این بخش لازم است با برنامه نویسی اندروید آشنایی کافی داشته باشید.

آموزش تست اپلیکیشن های اندرویدی

آموزش تست پروژه های اندرویدی

اپلیکیشن های اندرویدی بر روی دستگاه هایی اجرا می شوند که دارای حافظه و باتری محدودی بوده و پردازنده ی ضعیفی دارند. رفتار اپلیکیشن همچنین بر عوامل خارجی همچون اتصال به اینترنت، استفاده ی کلی از سیستم و غیره ... بستگی دارد. با توجه به آنچه گفته شد، اشکال زدایی، تست و بهینه سازی اپلیکیشن های اندرویدی بسیار مهم می باشد. انتخاب بخش هایی که باید تست شوند نیز حائز اهمیت بوده، به شما کمک می کند تا اپلیکیشن اندرویدی خود را بهبود ببخشید و در آینده عملیات تعمیر و نگهداشت آن را آسان می سازد.

از آنجایی که امکان تست اپلیکیشن های اندرویدی بر روی تمامی دستگاه ها (با تنظیمات و سخت افزار مختلف) وجود ندارد، توصیه می شود تست های نرم افزاری خود را بر روی دستگاه هایی با پیکربندی و سخت افزار معمول اجرا نمایید. لازم است اپلیکیشن خود را حداقل یکبار بر روی دستگاهی با پایین ترین قدرت سخت افزاری تست نمایید. هرچند بد نیست که اپلیکیشن مورد نظر را بر روی دستگاه هایی که بالاترین قدرت سخت افزاری را دارند نیز تست نمایید. برای مثال اپلیکیشن خود را بر دستگاه هایی که چگالی پیکسلی و وضوح تصویری بالایی دارند، تست کرده و مطمئن شوید که برنامه ی تحت موبایل شما با این پیکربندی نیز کاملا سازگار است.

Unit testing اپلیکیشن های اندرویدی می تواند به گروه های زیر تقسیم شود:

  • Local unit test (تست در بستر دستگاه مجازی جاوا) – تست هایی که می توان آن ها را در بستر JVM اجرا کرد در اصطلاح تست های local خوانده می شوند. تا حد امکان بهتر است از تست های local برای تست نرم افزار خود بهره بگیرید. تست های local به این علت که بر روی JVM اجرا می شوند، از لحاظ زمانی به خصوص نسبت به تست هایی که بر روی دستگاه واقعی اندروید اجرا می شوند، سریع تر به انجام می رسند.
  • Instrumented unit test(تست بر روی دستگاه واقعی اندروید) – تست هایی که بر روی یک سیستم واقعی اندروید انجام می شود. اگر می خواهید کدی را امتحان کنید که به API و توابع کتابخانه ای اندروید احتیاج دارد، در آن صورت لازم است این تست ها را بر روی دستگاه اندرویدی اجرا کنید. متاسفانه این امر سبب می شود زمان اجرای تست طولانی تر شود.
category of Android test

آموزش بخش هایی که در تست نرم افزاری، تمرکز بر روی آن قرار می گیرد

توصیه می شود در تست نرم افزار تمرکز خود را بیشتر بر روی آزمایش و کسب اطمینان از عملکرد صحیح منطق (logic) اپلیکیشن قرار دهید.

توصیه می شود تست اپلیکیشن خود را بر اساس قاعده ی زیر طراحی نمایید:

  • 1. 70-80 % از کل تست را به unit test اختصاص دهید تا بدین وسیله stability و ثبات code base تضمین شود.
  • 2. 20-30 % از تست را به صورت functional طراحی نموده و از کارکرد صحیح کد اطمینان کامل حاصل نمایید.
  • 3. و در صورتی که اپلیکیشن شما با کامپوننت هایی از سایر اپلیکیشن ها زیاد تعامل دارد، بخشی از کل تست را به صورت cross functional تعبیه نمایید.
بخش های تست نرم افزاری ندروید

در تست اپلیکیشن باید به این امر نیز توجه داشته باشید که آیا تنها خود اپلیکیشن مورد آزمایش قرار می گیرد یا رابطه ی (integration) اپلیکیشن با اپلیکیشن های دیگر نیز مورد بررسی قرار گرفته و آزمایش می شود. در صورتی که تنها خود اپلیکیشن مورد آزمایش است، می توانید از فریم ورک های اجرای تست استفاده نمایید که تنها به دانش جزئی از اپلیکیشن ها برای مثال view ID ها احتیاج است.

آموزش پیش شرط های تست گیری (testing preconditions)

بد نیست در تست اپلیکیشن های اندرویدی خود متدی به نام testPreconditions() داشته باشید که پیش شرط تمامی تست ها را بررسی می کند. چنانچه این متد در انجام وظیفه ی خود موفق نباشد، بلافاصله پی می برید که فرض یا پیش شرط های دیگر تست ها نیز نقض شده است.

آموزش ATSL و تست اپلیکیشن با ابزار JUnit 4

Android Testing Support Library (ATSL) به شما اجازه می دهد تا اپلیکیشن خود را با یک test runner سازگار با JUnit 4 مورد آزمایش قرار دهید. می توانید unit test های اپلیکیشن خود را بر روی JVM و به صورت محلی اجرا نمایید یا آن را در بستر دستگاه واقعی اندروید (Android runtime) راه اندازی کنید. علاوه بر آن، Google یک فریم ورک اجرای تست بر روی UI به نام Espresso تعبیه نموده که به شما امکان توسعه ی تست های رابط کاربری را می دهد.

برای فراهم آوردن امکان تست نرم افزار در محیط دستگاه مجازی جاوا (JVM)، افزونه ی Gradle یک نسخه ی خاص از فایل android.jar را ارائه می نماید (این فایل تحت عنوان Android mockable jar نیز شناخته می شود). فایل نام برده در اختیار کل unit test قرار گرفته تا تمامی فیلدها، متدها و کلاس ها در دسترس و قابل استفاده باشند. لازم به ذکر است که فراخوانی هر یک از توابع mockable JAR، به صورت پیش فرض، منجر به صدور خطا (exception) می شود.

بنابراین اگر کلاس های شما توابع کتابخانه ای اندروید (Android API) را فراخوانی نمی کنند، شما می توانید از فریم ورک تست گیری JUnit (یا هر فریم ورک دیگری که برای تست اپلیکیشن های جاوا ارائه شده) بدون محدودیت استفاده نمایید. چنانچه به توابع کتابخانه ای اندروید (android API) وابستگی (dependency) دارید (مجبورید از کتابخانه ها و کلاس های Android API استفاده نمایید)، در آن صورت این وابستگی ها در کد شما بایستی (برای unit test ها) از طریق فریم ورک های mocking (فریم ورک های ساختگی همچون Mockito) جایگزین و جبران گردد. برای مشاهده ی اطلاعات بیشتر جهت فعال سازی مقادیر بازگشتی برای متدهای ساختگی و شبیه سازی شده از فایل mockable.jar اندروید، به این آدرس مراجعه نمایید: Activating default return values for mocked methods in android.jar.

Android Testing Support Library این قابلیت را در اختیار توسعه دهنده قرار می دهد تا تست های نرم افزاری برای اپلیکیشن های خود طراحی کند. کتابخانه ی مذکور AndroidJUnitRunner، فریم ورک تست گیری Espresso و UI Automator را شامل می شود.

AndroidJUnitRunner امکان ساخت و اجرای تست های JUnit 4 را فراهم می کند، در حالی که چارچوب تست گیری Espresso برای طراحی تست ویژه ی لایه ی UI نرم افزار مناسب می باشد. با استفاده از UI Automator نیز برنامه نویس قادر خواهد بود تست های cross functional برای اپلیکیشن اندرویدی طراحی نماید.

AndroidJunitRunner توابع کتابخانه ای لازم برای اجرای تست نرم افزاری بر روی دستگاه حقیقی اندروید (Instrumentation API) را در قالب کلاس InstrumentationRegistry در اختیار توسعه دهنده قرار می دهد.

  • InstrumentationRegistry.getInstrumentation() تابعی است که در خروجی instrumentation فعلی و در حال اجرا را برمی گرداند.
  • InstrumentationRegistry.getContext() تابعی است که در خروجی Context پکیج instrumentation جاری را بازگردانی می نماید.
  • InstrumentationRegistery.getTargetContext() متدی است که Context اپلیکیشن مقصد را بازگردانی می نماید.
  • InstrumentationRegistry.getArguments()، یک کپی از مجموعه یا Bundle آرگومان هایی که به این instrumentation ارسال شده را در خروجی بازمی گرداند. این تابع زمانی به کار شما می آید که بخواهید به آرگومان های command line ارسال شده به instrumentation برای تست خود استفاده نمایید.

برای دسترسی و مدیریت lifecycle نیز می توانید از کلاس ActivityLifecycleMonitorRegistry استفاده نمایید.

آموزش ساختار پروژه ی اندرویدی و ایجاد پوشه ی تست

آموزش سازماندهی پروژه ی اندرویدی برای تست

برای سازمان دهی پروژه های تست اپلیکیشن، توسعه دهندگان معمولا از قرار داد خاصی پیروی می کنند. جهت سازمان دهی کد اپلکیشن، در این پروژه های اندرویدی توصیه می شود که از ساختار عنوان شده در زیر استفاده نمایید. این ساختار را ویزارد پروژه ی اندروید نیز به صورت جداگانه ارائه می دهد.

اگر از این قرارداد پیروی کنید، بی شک سیستم کامپایل اندروید (Android build system) قادر خواهد بود تست شما را به صورت خودکار بر روی دستگاه مورد نظر (JVM یا دستگاه واقعی اندروید) اجرا کند.

آموزش برطرف کردن خطای "error duplicate files in path"

در صورت برخورد با این خطا:"error duplicate files in path. Path in archive: LICENSE.txt" ، می توانید با افزودن قطعه کد زیر به فایل app/gradle.build ، به راحتی خطای مزبور را برطرف نمایید.

android {
    packagingOptions {
    exclude 'LICENSE.txt'
    }
}

آموزش اجرای Unit test بر روی JVM

آموزش اجرای Unit test بر روی نرم افزار در بستر Android runtime

Android برای تست هایی که (به صورت local) در محیط JVM یا در بستر دستگاه واقعی اندروید (Android runtime) اجرا می شوند، واژه ی unit tests را بکار می برد.

Unit test عبارت است از تستی که عملکرد و قابلیت یک کامپوننت نرم افزاری را به صورت جداگانه مورد آزمایش قرار می دهد.

اپلیکیشنی را در نظر بگیرید که در آن دکمه ای داخل یک activity، سبب فعال سازی و اجرای activity دیگری می شود. unit test ای که برای اپلیکیشن نوشته می شود، بررسی می کند آیا intent مورد نیاز (برای راه اندازی activity دوم) صادر می شود یا خیر. Unit test کاری با اینکه آیا activity دوم در نتیجه ی فراخوانده شدن intent اجرا می شود یا خیر، ندارد.

Unit test ها بر اساس ورژن ویرایش شده از کتابخانه ی اندروید android.jar اجرا می شوند. در این ویرایش تمامی modifier های final حذف شده اند. این امر به شما امکان می دهد تا از کتابخانه های شبیه سازی و ایجاد آبجکت های ساختگی (mocking library) همچون Mockito بهره بگیرید.

آموزش محل جایگذاری unit test ها در پروژه ی اندرویدی

همان طور که قبلا ذکر شد، unit test های پروژه ی اندروید باید در پوشه ی app/src/test قرار داده شوند.

آموزش کتابخانه ها/dependency های الزامی در فایل Gradle build

برای استفاده و اجرای تست های JUnit بر روی کامپوننت های نرم افزاری مربوط به اپلیکیشن خود، بایستی آن ها را در قالب dependency (کتابخانه) به فایل Gradle build خود اضافه نمایید.

dependencies {
    // Unit testing dependencies
    testCompile 'junit:junit:4.12'
    // Set this dependency if you want to use the Hamcrest matcher library
    testCompile 'org.hamcrest:hamcrest-library:1.3'
    // more stuff, e.g., Mockito
}

آموزش اجرای unit test ها از طریق سیستم کامپایل Gradle

Unit test های خود را با فراخوانی دستور gradlew test اجرا نمایید.

آموزش اجرای unit test ها از محیط کاری Android Studio

به منظور اجرای یک unit test، بر روی کلاس تست گیری (test class) در پنجره ی Project راست کلیک کرده و سپس گزینه ی Run را انتخاب نمایید.

اجرای unit test در android

آموزش محل قرارگیری نتایج و گزارش های مربوط به تست (test reports)

گزارش ها و نتایج مربوط به تست در پوشه ی app/build/reports/tests/debug/`directory ایجاد می شوند. فایل `index.html اطلاعاتی درباره ی هر یک از صفحات تست فراهم آورده و به آن ها جهت مشاهده ی اطلاعات بیشتر لینک می دهد.

آموزش فعال سازی مقادیر بازگشتی پیش فرض متدهای ساختگی (mocked methods) در فایل android.jar

به طور پیش فرض، فراخوانی هر یک از توابع موجود در فایل android.jar سبب رخداد خطا (exception) می شود. این وضعیت پیش فرض باعث می شود که unit test ها تنها کد شما را تست کنند و به هیچ یک از رفتار خاص محیط یا بستر اجرای اندروید (Android platform) وابسته نباشند. در صورت نیاز به تنظیم اختصاصی یک قابلیت یا رفتار خاص، کافی است از فریم ورک های شبیه ساز (mocking framework) جهت جایگزینی این متدها و فراخوانی آن ها استفاده نمایید.

همچنین می توانید به سیستم کامپایل Gradle دستور بدید که به عنوان خروجی متدهای فراخوانی شده، مقادیر پیش فرض مورد نظر را بازگردانی نمایند. برای این منظور کافی است تنظیمات زیر را در فایل Gradle build اپلیکیشن خود لحاظ نمایید.

android {
  // ...
  testOptions {
    unitTests.returnDefaultValues = true
  }
}
آموزش تمرین: طراحی unit test جهت تست اپلیکیشن

آموزش مرحله ی آماده سازی: ساخت پروژه ی اندروید

پروژه ی اندرویدی خود را بر اساس برنامه ای که در مباحث قبلی ساختید (Android temperature converter)، طراحی نمایید.

آموزش ساخت unit test

هدف از انجام این تمرین

طی تمرین حاضر قادر خواهید بود یک تست JUnit4 برای پروژه ی اندرویدی خود بنویسید.

آموزش افزودن dependency / کتابخانه ی JUnit

Dependency یا کتابخانه ی Junit را در فایل app/build.gradle جایگذاری نمایید. اگر پوشه ی test در پروژه ی شما موجود نبود، در آن صورت بایستی طبق فرایندی که قبلا توضیح داده شد (نحوه ی ساخت پوشه ی تست در محیط کاری Android Studio)) را طی نموده و پوشه ی مورد نیاز را ایجاد نمایید.

                        dependencies {
        // Unit testing dependencies
        testCompile 'junit:junit:4.12'
}

آموزش ساخت و طراحی تست

در پوشه ی app/src/test، دو متد تست گیری (test method) زیر را داخل کلاس ConverterUtil تعریف نمایید.

package com.vogella.android.temperature.test;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.vogella.android.temperature.ConverterUtil;
public class ConverterUtilTest {
        @Test
        public void testConvertFahrenheitToCelsius() {
                float actual = ConverterUtil.convertCelsiusToFahrenheit(100);
                // expected value is 212
                float expected = 212;
                // use this method because float is not precise
                assertEquals("Conversion from celsius to fahrenheit failed", expected,
                                actual, 0.001);
        }
        @Test
        public void testConvertCelsiusToFahrenheit() {
                float actual = ConverterUtil.convertFahrenheitToCelsius(212);
                // expected value is 100
                float expected = 100;
                // use this method because float is not precise
                assertEquals("Conversion from celsius to fahrenheit failed", expected,
                                actual, 0.001);
        }
}

آموزش اجرای unit test بر روی پروژه

بد نیست با تست unit test هایی که برای بخش های مختلف پروژه ی خود نوشته اید، اطمینان حاصل نمایید که تست ها به درستی پیاده سازی شده اند. تست های شما بایستی طبق انتظار به درستی اجرا شوند.

طراحی instrumentation test برای اجرای تست بر روی اپلیکیشن در بستر دستگاه حقیقی اندروید

آموزش Instrumentation test

API یا توابع کتابخانه ای تست گیری اندروید hook هایی را برای وصل شدن به کامپوننت های نرم افزاری و lifecycle اپلیکیشن اندروید فراهم می آورند (hook مکان یا interface ای است که در قالب کد پکیج شده ارائه شده و به برنامه نویس اجازه می دهد تا کدهایی با تنظیمات اختصاصی و دلخواه وارد متن برنامه کند. به طور مثال، توسعه دهنده می تواند کدی وارد برنامه کند که وظیفه ی آن بررسی این است که هر چند وقت یکبار یک مسیر منطقی داخل اپلیکیشن مورد نظر طی می شود).

این hook ها در اصطلاح instrumentation API خوانده شده و به تست های شما این اجازه را می دهد تا اداره ی lifecycle و event های مرتبط با تعامل کاربر را بدست بگیرند.

در شرایط معمولی، اپلیکیشن نمی تواند event های مربوط به lifecycle را کنترل کند و در این برهه کاربر مدیریت جریان یا روند اپلیکیشن را بدست می گیرد. برای مثال، زمانی که سیستم اندروید کامپوننت activity اپلیکیشن را اجرا می کند، در واقع متد onCreate() فراخوانده می شود. یا هنگامی که کاربر دکمه ای را در نمایشگر فشار می دهد، کد مربوطه از اپلیکیشن فراخوانده و اجرا می شود. توسعه دهنده می تواند با بهره گیری از توابع کتابخانه ای Instrumentation، رخدادهایی از این دست را با اجرای کد آزمایشی (test code) خود اداره کند. سپس می تواند متد finish() را صدا زده، activity را مجددا اجرا کند و بدین وسیله مطمئن شود که اطلاعات مربوط به وضعیت (instance state) activity مزبور پس از راه اندازی مجدد بازگردانی می شوند یا خیر.

Unit test هایی که زیرمجموعه ی instrumentation هستند، همان طور که پیش تر نیز ذکر شد، بجای JVM (دستگاه مجازی جاوا)، بر روی دستگاه های حقیقی اندروید یا در بستر نرم افزار شبیه ساز (emulator) اجرا می شوند. این تست ها به دستگاه واقعی اندروید و منابع آن دسترسی داشته و برای اجرای تست بر روی کامپوننت های مجزای نرم افزار و انجام unit test بسیار ساده می باشند. همان طور که می دانید، تست قابلیت های اپلیکیشن به واسطه ی unit test با بهره گیری از فریم ورک های شبیه سازی (mocking frameworks) به راحتی امکان پذیر نمی باشد. به عنوان مثال می توان به تستی اشاره کرد که کد و پیاده سازی یک Parcelable را آزمایش کرده و سعی می کند از عملکرد صحیح آن اطمینان حاصل نماید.

کلاس تست گیری که مبتنی بر instrumentation می باشد، این امکان را به شما می دهد تا event های اصلی (event های مربوط به نمایشگر/touch event) را به اپلیکیشن مورد تست ارسال نمایید

با وجود فریم ورک های تست گیری همچون Espresso که قابلیت آزمایش UI را فراهم می آورند، توسعه دهنده به ندرت مجبور می شود تا مستقیما از instrumentation API استفاده نماید.

آموزش سیستم اندروید چگونه تست ها را اجرا می کند

Test runner (کلاس تست گیری) پایه برای اجرای تست های نرم افزاری بر روی اپلیکیشن های اندرویدی، InstrumentationTestRunner است. این کلاس تمامی متدهای تست گیری را اجرا کرده و در حافظه بارگذاری می نماید. سپس به واسطه ی instrumentation API با سیستم اندروید ارتباط برقرار می کند. زمانی که تستی را جهت آزمایش اپلیکیشن اندرویدی خود اجرا می کنید، سیستم اندروید تمامی فرایندهای مربوط اپلیکیشن مورد آزمایش (که در حال تست شدن هستند) را از حافظه پاک کرده و متعاقبا یک نمونه (instance) جدید از آن را در حافظه بارگذاری می کند. لازم به توضیح است که کلاس مزبور اپلیکیشن را راه اندازی نمی کند، بلکه این امر، وظیفه ی متدهای تست گیری می باشد. به عبارت دیگر این متد تست گیری است که اداره ی lifecycle کامپوننت های نرم افزاری را بدست می گیرد.

کلاس تست گیری مورد نظر، به هنگام راه اندازی و مقداردهی اولیه (initialization)، همچنین متد onCreate() مربوط به اپلیکیشن و activity مورد تست را صدا می زند.

آموزش شبیه سازی رفتار آبجکت ها در اندروید (ایجاد آبجکت های ساختگی جهت تست)

توسعه دهنده برای اجرای تست های instrumentation بر روی نرم افزار اندرویدی خود، همچنین می تواند از فریم ورک شبیه سازی (mocking framework) Mockito استفاده نماید. با بهره گیری از این فریم ورک توسعه قادر خواهد بود آن بخش هایی از سیستم اندروید را که تست آن ها ضرورتی ندارد، با بخش هایی از این فریم ورک جایگزین نماید. ویرایش های قبلی چارچوب نرم افزاری اندروید (Android framework) کلاس های شبیه سازی /mocking classes را ویژه ی تست بخش های اپلیکیشن ارائه می دادند. اما امروزه با وجود Mockito این کلاس ها دیگر کاربردی ندارند.

آموزش محل قرارگیری تست های

همان طور که قبلا توضیح داده شد (در بخش سازماندهی پروژه و تست ها)، unit test ها بایستی در پوشه ی app/src/androidTest/java قرار گیرد.

آموزش تعریف dependency ها و testInstrumentationRunner داخل فایلGradle build

جهت استفاده از JUnit برای اپلیکیشن های اندرویدی خود، می بایست dependency (کتابخانه ی مورد نیاز) را به فایل Gradle build اضافه نمایید. علاوه بر آن لازم است android.support.test.runner.AndroidJUnitRunner را نیز در قالب کلاس testInstrumentationRunner داخل فایل build تعریف نمایید.

defaultConfig {
..... more stuff
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
dependencies {
    // Unit testing dependencies
    androidTestCompile 'junit:junit:4.12'
    // Set this dependency if you want to use the Hamcrest matcher library
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    // more stuff, e.g., Mockito
}

آموزش استفاده از @RunWith(AndroidJUnit4.class)

توصیه می شود تست را با دستور @RunWith(AndroidJUnit4.class) نیز نشانه گذاری (annotate) نمایید. AndroidJUnit4 از JUnit4 ارث بری می کند (extend می کند). بنابراین اگر از syntax/ساختار نگارشی خالص junit4 و ActivityTestRule استفاده نمایید، دیگر لزومی ندارد تست را با annotation فوق نشانه گذاری نمایید. اما در صورتی که لازم باشد تست های فریم ورک Espresso را همراه با ActivityTestRule و JUnit4 اجرا نمایید، استفاده از آن الزامی خواهد بود.

آموزش اجرای unit test از طریق سیستم کامپایل Gradle

Unit test را با فراخوانی دستور gradlew connectedCheck اجرا نمایید.

آموزش اجرای unit test از داخل محیط برنامه نویسی Android Studio

بر روی کلاس تست گیری در پنجره ی Project راست کلیک کرده و سپس گزینه ی Run را انتخاب نمایید.

آموزش محل جایگذاری گزارش های تست

گزارش های تست در پوشه ی app/build/reports/androidTests/connected/ جایگذاری می شود.

فایل index.html اطلاعات مختصری درباره ی تست ارائه داده و به تمامی صفحات آن لینک می دهند.

نحوه ی جایگزین کردن بخش های اپلیکیشن با instrumentation test ها

می توانید برای اجرای تست های مبتنی بر instrumentation و جایگزین کردن کلاس اپلیکیشن (با تست های instrumentation)، کلاس AndroidJUnitRunner و newApplication را بازنویسی (override) نمایید.

package com.vogella.android.daggerjunitmockito;
import android.app.Application;
public class MyMockApplication extends Application {
    @Override
    public void onCreate() {
       // do something important for your tests here
    }
}

آموزش Test runner:

package com.vogella.android.daggerjunitmockito;
import android.app.Application;
import android.content.Context;
import android.support.test.runner.AndroidJUnitRunner;
public class MockTestRunner extends AndroidJUnitRunner {
  @Override
  public Application newApplication(ClassLoader cl, String className, Context context)
      throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return super.newApplication(cl, MyMockApplication.class.getName(), context);
  }
}

همچنین لازم است این test runner را در فایل build.gradle ثبت (تعریف) نمایید.

android {
    /// more
        testInstrumentationRunner "com.vogella.android.daggerjunitmockito.MockTestRunner"
    }
    /// more
}
تمرین: شبیه سازی و کسب اطمینان از عملکرد صحیح قابلیت دسترسی به فایل (Mocking file access)

هدف از این تمرین

لازم نیست این کلاس را در اپلیکیشن اندرویدی خود بکار ببرید و در اینجا تنها به منظور نمایش نحوه ی استفاده از Mockito در unit test بکار برده شده است.

آموزش ایجاد کلاسی جهت تست گیری

در اپلیکیشن اندرویدی آماده ی خود و یا پروژه ای با پکیج com.vogella.android.testing.mockitocontextmock، کلاس زیر را پیاده سازی نمایید.

public class Util {
        public static void writeConfiguration(Context ctx ) {
                try (FileOutputStream openFileOutput =
                         ctx.openFileOutput( "config.txt", Context.MODE_PRIVATE);) {
                        openFileOutput.write("This is a test1.".getBytes());
                        openFileOutput.write("This is a test2.".getBytes());
                } catch (Exception e) {
                        // not handled
                }
        }
}

آموزش ساخت unit test جدید

با استفاده از فریم ورک شبیه سازی (mocking framework) Mockito، یک unit test جدید بنویسید که موارد زیر را بررسی می کند:

  • 1. openFileOutput دقیقا یکبار فراخوانی شود.
  • 2. متد write() حداقل دوبار صدا زده شود.
package com.vogella.android.testing.mockitocontextmock;
import android.content.Context;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.FileOutputStream;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class TextContextOutputStream {
@Mock
Context context;
@Mock
FileOutputStream fileOutputStream;
@Before
public void init(){
    MockitoAnnotations.initMocks(this);
}
    @Test
public void writeShouldWriteTwiceToFileSystem() {
    try {
        when(context.openFileOutput(anyString(), anyInt())).thenReturn(fileOutputStream);
        Util.writeConfiguration(context);
        verify(context, times(1)).openFileOutput(anyString(), anyInt());
        verify(fileOutputStream, atLeast(2)).write(any(byte[].class));
    } catch (Exception e) {
        e.printStackTrace();
        fail();
    }
}
}

اطلاعات بیشتر در خصوص اجرای تست بر روی اپلیکیشن های اندرویدی

آموزش کلاس های assert

توابع کتابخانه ای اجرای تست (Android testing API) بر روی اپلیکیشن های اندرویدی، علاوه بر JUnit Assert، دو کلاس دیگر به نام های MoreAsserts و ViewAsserts ارائه می دهد.

آموزش Test group ها (گروه بندی تست ها)

دستورات @SmallTest، `@MediumTest و `@LargeTest`annotations امکان گروه بندی تست ها را برای توسعه دهنده فراهم می کنند. به عبارتی روشن تر این قابلیت توسعه دهنده را قادر می سازد تا تست ها را بر اساس ویژگی خاصی گروه بندی نماید. برای مثال تنها آن دسته از تست هایی را بر روی نرم افزار اجرا کند که زمان اجرای آن ها چندان طولانی نیست یا تست هایی که زمان اجرای آن ها طولانی هستند را بر روی سرویس دهنده ی continuous integration به اجرا بگذارند (Continuous integration به یک سری روش گفته می شود که طی آن سلامت نرم افزار به طور دایم تضمین می شود، به طوری که خورده تغییرات افرادِ درگیر در پروژه باعث رخداد خطا در کل پروژه نشده و به درستی مثل تکه های پازل در کنار هم قرار گیرند).

برای اینکه تنها تست های انتخابی اجرا شوند، شما می توانید کلاس InstrumentationTestRunner را به واسطه ی افزونه ی (plug-in) Gradle تنظیم نمایید. کد زیر نمونه ای از فایل build.gradle را نمایش می دهد که با دستور @SmallTests علامت گذاری شده و به همین خاطر تنها تست هایی که زمان اجرای طولانی ندارند را اجرا می کند.

android {
  //....
  defaultConfig {
  //....
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    testInstrumentationRunnerArgument "size", "small"
  }
}
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Test;
public class ExampleTest {
    @Test
    @SmallTest
    public void validateSecondActivity() {
        // Do something not so long...
    }
    @Test
    @MediumTest
    public void validateSecondActivityAgain() {
        // Do something which takes more time....
    }
}

آموزش فیلتر کردن تست

می توانید تست های نرم افرازی خود را با annotation ها نشانه گذاری کرده و از این طریق تست های مزبور را فیلتر نمایید (برای مثال شرایطی که تست در آن اجرا می شود را دقیق مشخص کنید). در واقع کلاس اجرای تست های نرم افزاری اندروید (android test runner) به شما امکان می دهد این تست ها را به وسیله ی annotation های زیر، محدود ساخته و صریحا مشخص کنید که تست با چه شرایطی بایستی اجرا شود.

Annotation های فیلتر و محدود سازی تست
Annotation
شرح کاربرد
@RequiresDevice
تعیین می نماید که تست تنها بایستی بر روی دستگاه های واقعی اندروید اجرا شود. چنانچه محیط اجرای تست شبیه ساز باشد، تست به واسطه ی این annotation اجرا نخواهد شد.
@SdkSupress
@SDKSupress(minSdkVersion=18)

دستور @FlakyTest

می توانید با استفاده از دستور @FlakyTest به سیستم اندروید اعلان نمایید که تست را در صورت عدم موفقیت، بار دیگر اجرا کند. Annotation ذکر شده، یک attribute به نام tolerance دارد که با مقدار دهی آن می توانید تعیین نمایید که تست پس از چندبار ناموفق بودن در اجرا، failed در نظر گرفته شده و دیگر تکرار نشود.

تمرین: تست lifecycle

هدف از این تمرین

در این تمرین، استفاده از فریم ورک تست گیری نرم افزار در بستر دستگاه حقیقی اندروید (instrumentation test) به صورت عملی نمایش داده شده است. البته برای چنین تستی، بهتر است از فریم ورک های سطح بالاتر همچون Espresso استفاده نمایید.

آموزش ایجاد پروژه و تست آن

یک پروژه و activity جدید به ترتیب به نام های com.vogella.android.test.simpleactivity و MainActivity تعریف نمایید. ¬¬

حال activity دیگری به نام SecondActivity تعریف نموده و به پروژه ی نام برده اضافه نمایید. این activity بایستی از یک layout استفاده نموده و حداقل یک آبجکت TextView دربرداشته باشد. id این آبجکت بایستی بر روی "resultText" تنظیم شده و text آن با "Started" مقداردهی شده باشد.

یک فیلد EditText به فایل layout کلاس MainActivity اضافه نمایید.

یک آبجکت دکمه به فایل layout ای که مورد استفاده ی کلاس MainActivity می باشد، اضافه نمایید. زمانی که دکمه کلیک می شود، دومین activity بایستی اجرا شود.

فیلد متنی EditText را با استفاده از "text" به عنوان کلید آن، داخل آبجکت intent قرار دهید. پس از آن رشته ی http://www.vogella.com را به عنوان extra (اطلاعات اضافی که بین دو activity مبادله می شود) با انتخاب "URL" به عنوان کلید، (به وسیله ی دستور intent.putExtra("URL", "http://www.vogella.com");) داخل آبجکت intent قرار دهید.

در زیر نمونه کدی از پیاده سازی MainActivity را مشاهده می کنید.

package testing.android.vogella.com.simpleactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void onClick(View view) {
        Intent intent = new Intent(this, SecondActivity.class);
        intent.putExtra("URL", "http://www.vogella.com");
        startActivity(intent);
    }
}

آموزش ساخت کلاس و پروژه ی تست

یک instrumentation test دیگر مشابه زیر پیاده سازی نمایید.

package com.vogella.android.test.simpleactivity.test;
import android.app.Activity;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.test.ActivityInstrumentationTestCase2;
import android.test.TouchUtils;
import android.test.UiThreadTest;
import android.test.ViewAsserts;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.vogella.android.test.simpleactivity.R;
import com.vogella.android.test.simpleactivity.MainActivity;
import com.vogella.android.test.simpleactivity.SecondActivity;
public class SecondActivityFunctionalTest extends
                ActivityInstrumentationTestCase2 {
        private static final String NEW_TEXT = "new text";
        public SecondActivityFunctionalTest() {
                super(SecondActivity.class);
        }
        public void testSetText() throws Exception {
                SecondActivity activity = getActivity();
                // search for the textView
                final TextView textView = (TextView) activity
                                .findViewById(R.id.resultText);
                // set text
                getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                                textView.setText(NEW_TEXT);
                        }
                });
                getInstrumentation().waitForIdleSync();
                assertEquals("Text incorrect", NEW_TEXT, textView.getText().toString());
        }
        @UiThreadTest
        public void testSetTextWithAnnotation() throws Exception {
                SecondActivity activity = getActivity();
                // search for the textView
                final TextView textView = (TextView) activity
                                .findViewById(R.id.resultText);
                textView.setText(NEW_TEXT);
                assertEquals("Text incorrect", NEW_TEXT, textView.getText().toString());
        }
}

آموزش استفاده از ابزار Monkey جهت ایجاد و ارسال شبه event به دستگاه

توضیحی درباره ی ابزار monkey

Monkey یک ابزار command-line است که بر روی محیط شبیه ساز یا دستگاه حقیقی اندروید اجرا شده و این قابلیت را به شما می دهد تا علاوه بر مجموعه ای از event هایی که توسط کاربر فرخوانی می شوند، تعدادی از event هایی که در سطح سیستم رخ می دهند را نیز به صورت شبه تصادفی ایجاد و به سیستم ارسال نمایید. در واقع شما با این ابزار قادر خواهید بود اپلیکیشن های در حال توسعه ی خود را به صورت تصادفی اما تکرار پذیر، تحت شرایط سنگین آزمایش نموده و مطمئن شوید که اپلیکیشن مورد نظر در صورت وجود بار کاری زیاد نیز دارای قابلیت دسترسی و مدیریت خطای بالایی می باشد (stress-test).

شما می توانید ابزار مزبور را طوری تنظیم کنید که تنها برای پکیج خاصی اجرا شده و صرفا اپلیکیشن مد نظر را تست کند.

آموزش نحوه ی استفاده از ابزار Monkey

دستور زیر 2000 رخداد تصادفی به اپلیکیشنی که اسم پکیج de.vogella.android.test.target را در بالای فایل خود دارند، ارسال می کند.

adb shell monkey -p de.vogella.android.test.target -v 2000

لازم به ذکر است که Monkey در برخی شرایط مشکلاتی را برای سرویس دهنده ی adb ایجاد می کند. در چنین موقعیتی می توانید با اجرای دو دستور زیر، سرویس دهنده ی adb را مجددا راه اندازی نموده و مشکل را برطرف کنید.

adb kill-server
adb start-server

برای اینکه مطمئن شوید توالی یا مجموعه event های تولید شده همیشه یکسان می باشد، باید پارامتر -s [seed] را به دستور اضافه نمایید.

آموزش تست آبجکت Application

در اندروید، منطق اپلیکیشن، داده ها و تنظیمات مربوط به کل برنامه داخل آبجکتی به نام application جای گرفته است. لازم است این آبجکت را تست کرده و از عملکرد صحیح آن اطمینان حاصل نمایید.

می توانید یک تست JUnit 4 برای آزمایش عملکرد اپلیکیشن نوشته و آن را بر روی JVM اجرا نمایید. در این سناریو بایستی تمامی dependency و کتابخانه ی های مربوط به آبجکت اپلیکیشن را شبیه سازی (mock) نمایید.

برای تست آبجکت application در runtime اندروید، می بایست از کلاس ApplicationTestCase استفاده نمایید. در آینده انتظار می رود که شرکت گوگل یک rule جدید برای تست آبجکت یاد شده، در کتابخانه ی JUnit4 تعبیه نماید، اما در زمان حاضر چنین امکانی برای استفاده ی توسعه دهنده فراهم نیست.

/ test runner کلاس اجرای تست محیط Android (InstrumentationTestRunner) در مرحله ی مقداردهی اولیه، به صورت خودکار یک نمونه از کلاس application می سازد. بنابراین در صورت استفاده از پردازش ناهمزمان در بدنه ی متد onCreate()، لازم است حتما این نکته را به خاطر داشته باشید.

تمرین: تست اپلیکیشن

آموزش ایجاد پروژه

یک اپلیکیشن جدید اندروید با اسم پکیج com.vogella.android.testing.applicationtest بر اساس قالب آماده ی (template) Empty Activity ایجاد نمایید.

آبجکت application را با پیاده سازی زیر به برنامه ی خود اضافه نمایید.

package com.vogella.android.testing.applicationtest;
import android.app.Application;
import java.util.ArrayList;
import java.util.List;
public class MyApplication extends Application {
    public static final List list = new ArrayList();
}

لازم است اپلیکیشن را در لایه ی XML، داخل فایل manifest نیز اعلان نمایید.








            
        
    

آموزش تعریف unit test برای تست آبجکت application

داخل پوشه ی app/src/test یک unit test جدید تعریف نمایید. تست را طوری بنویسید که انتظار داشته باشد (assert) فیلد MyApplication.list تهی نبوده و اندازه ی (size) اولیه ی آن 0 باشد.

آموزش طراحی instrumentation test برای آبجکت application

یک unit test جدید بر اساس فریم ورک تست گیری JUnit 3 به صورت زیر تعبیه نمایید.

package com.vogella.android.testing.applicationtest;
import android.content.pm.PackageInfo;
import android.test.ApplicationTestCase;
import android.test.MoreAsserts;
public class ApplicationTest extends ApplicationTestCase {
    private MyApplication application;
    public ApplicationTest() {
        super(MyApplication.class);
    }
    protected void setUp() throws Exception {
        super.setUp();
        createApplication();
        application = getApplication();
    }
    public void testCorrectVersion() throws Exception {
        PackageInfo info = application.getPackageManager().getPackageInfo(application.getPackageName(), 0);
        assertNotNull(info);
        MoreAsserts.assertMatchesRegex("\\d\\.\\d", info.versionName);
    }
}

آموزش تست سایر کامپوننت های نرم افزاری اندروید

آموزش تست سرویس

جهت تست یک سرویس، بایستی از کلاس ServiceTestRule که از Testing Support Library اندروید مشتق می شود، استفاده نمایید. در ویرایش های قبلی به منظور آزمایش کامپوننت سرویس از ServiceTestCase استفاده می شد که با ارائه ی کلاس نام برده، استفاده از آن منسوخ شد.

این rule یک مکانیزم ساده در اختیار توسعه دهنده قرار می دهد که سرویس را قبل از اجرای تست راه اندازی نموده و با اتمام آن، کامپوننت مورد نظر را متوقف می سازد. در واقع این rule تضمین می کند که سرویس به هنگام اجرا به طور کامل متصل شده باشد. می توانید سرویس را به واسطه ی یکی از توابع کمکی (helper method) فراخوانی نمایید. زمانی که تست پایان می یابد، سرویس به صورت خودکار متوقف شده و به همراه آن تمامی متدهایی که با @After نشانه گذاری شده اند نیز خاتمه می یابند.

نکته :

این rule همراه با IntentService قابل استفاده نمی باشد چرا که به مجرد اتمام اجرای متد onHandleIntent، به طور خودکار از حافظه پاک می گردد.

کد زیر یک سرویس را تست می کند.

@RunWith(AndroidJUnit4.class)
@MediumTest
public class MyServiceTest {
    @Rule
    public final ServiceTestRule mServiceRule = new ServiceTestRule();
    // test for a service which is started with startService
    @Test
    public void testWithStartedService() {
        mServiceRule.
        startService(new Intent(InstrumentationRegistry.getTargetContext(),
                        MyService.class));
        // test code
    }
    @Test
 // test for a service which is started with bindService
    public void testWithBoundService() {
        IBinder binder = mServiceRule.
                        bindService(new Intent(InstrumentationRegistry.getTargetContext(),
                                        MyService.class));
        MyService service = ((MyService.LocalBinder) binder).getService();
        assertTrue("True wasn't returned", service.doSomethingToReturnTrue());
    }
}
adb kill-server
adb start-server

آموزش تست کامپوننت نرم افزاری Content provider

به منظور تست content provider، توسعه دهنده می تواند از کلاس ProviderTestCase2 استفاده کند. این کلاس به صورت خودکار از کامپوننت (content provider) مورد تست، نمونه سازی کرده و آبجکت IsolatedContext را در آن درج می نماید. گرچه این context یا بستر از سیستم اندروید مجزا است، اما همچنان اجازه ی دسترسی به فایل و دیتابیس را فراهم می کند. در واقع استفاده از آبجکت IsolatedContext این اطمینان را به وجود می آورد که provider مورد تست، تاثیری بر روی دستگاه واقعی اندروید نمی گذارد.

کلاس نام برده همچنین با ارائه ی متدی به نام getMockContentResolver() امکان دسترسی به کلاس MockContentResolver را فراهم می سازد.

شما بایستی تمامی عملیات مربوط به provider را آزموده و همچنین بررسی نمایید که در صورت فراخوانی provider با URI یا projection غیرمجاز چه اتفاقی رخ می دهد.

آموزش تست Loader

جهت تست کارکرد یک loader، توسعه دهنده می بایست از کلاس `LoaderTestCase` استفاده نماید. البته در آینده ی نزدیک انتظار می رود که شرکت گوگل با معرفی یک rule جدید در کتابخانه ی JUnit4، کلاس نام برده را منسوخ نماید.

آموزش Annotation های متفرقه یا پشتیبان

علاوه بر annotation های نام برده، شرکت گوگل یک پکیج حاوی annotation های جدید در اختیار توسعه دهنده قرار می دهد. پکیج مزبور تعدادی annotation metadata (دستوراتی که اطلاعاتی را درباره ی عملکرد کد شرح می دهند) ارائه می دهد که با استفاده از آن ها در متن برنامه، توسعه دهنده قادر است از رخداد خطا در عملکرد اپلیکیشن جلوگیری نماید.

جهت استفاده از این annotation ها، کافی است کتابخانه ی (dependency) زیر را به فایل Gradle build خود اضافه نمایید.

dependencies {
    compile 'com.android.support:support-annotations:22.2.0'
}

جهت دریافت اطلاعات بیشتر درباره ی annotation ها می توانید به آدرس http://tools.android.com/tech-docs/support-annotations مراجعه نمایید.

آموزش Annotation های مربوط به Nullness

با درج دستور @Nullable در بالای کد توسعه دهنده به سیستم اعلان می کند که یک پارامتر یا مقدار بازگشتی می تواند null باشد. به طور مشابه، دستور @NonNull در بالای کد مربوطه نشانگر این است که یک پارامتر (یا مقدار بازگشتی) نمی تواند و نباید null باشد.

آموزش Annotation های مربوطه به thread

در صورتی که یک متد تنها از thread خاصی قابل فراخوانی باشد، می توانید آن را با یکی از annotation های زیر نشانه گذاری نمایید.

  • @UiThread
  • @MainThread
  • @WorkerThread
  • @BinderThread
مثال:
@WorkerThread
protected abstract Result doInBackground(Params... params);
@MainThread
  protected void onProgressUpdate(Progress... values) {
  }

آموزش تهیه ی گزارش از مقدار کد تست شده از کل پروژه (Code coverage report)

Code coverage report بیانگر این است چه میزان از کل کد اپلیکیشن شما توسط unit test مورد آزمایش قرار گرفته است. به منظور تهیه ی چنین گزارشی، می توانید تنظیمات جداگانه ای برای اجرا (launch config) آن تعبیه نمایید. برای نیل به این هدف، پکیج مورد نظر خود را انتخاب نموده و پس از باز نمودن منو، آیتم [Create Tests in…] را انتخاب نمایید.

ریپورت گیری

حال می توانید تنظیمات (config) runtime جدید برای code coverage یا میزان کد مورد آزمایش خود ایجاد نمایید. پس از اجرا، یک گزارش تهیه شده و به نمایش گذاشته می شود.

کانفیگ setting run time demo

آموزش ساخت پوشه ی تست در محیط کاری Android Studio

ویرایش های جدید محیط برنامه نویسی Android Studio، یک پوشه ی تست (test folder) به قالب آماده ی پروژه ی پیش فرض (project template) خود اضافه کرده است. در صورت استفاده از قالبی که قابلیت ایجاد پوشه ی تست را به صورت درون ساخته ندارد، بایستی این پوشه را خود به صورت دستی ایجاد نمایید. جهت ایجاد پوشه ی تست در محیط کاری مذکور، کادر Project را باز نمایید. در این view می توانید ساختار درختی و directory structure پروژه ی خود را مشاهده نمایید.

فایل تست در آندروید

Src را انتخاب نموده و پس از باز کردن Context menu یک پوشه ی تست (test folder) جدید ایجاد نمایید.

یافتن فایل تست در اندروید جایابی فایل تست در اندروید نهایی کردن جایابی فایل تست در اندروید

در صورتی که همچنان کامل انجام نشده باشد، کتابخانه ی (dependency) JUnit را به فایل Gradle build اضافه نمایید.

dependencies {
    // Unit testing dependencies
    testCompile 'junit:junit:4.12'
    // Set this dependency if you want to use the Hamcrest matcher library
    testCompile 'org.hamcrest:hamcrest-library:1.3'
    // more stuff, e.g., Mockito
}
توجه :

لازم به ذکر است که در نتیجه ی ساخت پوشه ی Java، ممکن است پوشه ی جدید تست به عنوان source file به فایل gradle.build اضافه گردد. چنانچه دستور زیر در فایل app/build.gradle موجود باشد، در آن صورت می بایست کد ذکر شده را کاملا از فایل حذف نمایید. تست نباید به هیچ وجه به عنوان یک source folder معمولی در نظر گرفته شود.

sourceSets { main { java.srcDirs = ['src/main/java', 'src/test/java/'] } }

پس از آن کافی است unit test خود را به این ساختار درختی پوشه ها یا folder structure اضافه نمایید.

مبحث حاضر به شما آموزش می دهد چگونه می توانید برای اپلیکیشن های اندرویدی خود instrumentation & unit test طراحی نموده و از عملکرد صحیح بخش های مختلف پروژه خود اطمینان حاصل نمایید. سپس برای شما شرح می دهد چگونه این تست ها را در محیط کاری Android Studio و با استفاده از سیستم کامپایل Gradle اجرا نمایید.

برای یادگیری این بخش لازم است با برنامه نویسی اندروید آشنایی کافی داشته باشید.

آموزش تست اپلیکیشن های اندرویدی

آموزش تست پروژه های اندرویدی

اپلیکیشن های اندرویدی بر روی دستگاه هایی اجرا می شوند که دارای حافظه و باتری محدودی بوده و پردازنده ی ضعیفی دارند. رفتار اپلیکیشن همچنین بر عوامل خارجی همچون اتصال به اینترنت، استفاده ی کلی از سیستم و غیره ... بستگی دارد. با توجه به آنچه گفته شد، اشکال زدایی، تست و بهینه سازی اپلیکیشن های اندرویدی بسیار مهم می باشد. انتخاب بخش هایی که باید تست شوند نیز حائز اهمیت بوده، به شما کمک می کند تا اپلیکیشن اندرویدی خود را بهبود ببخشید و در آینده عملیات تعمیر و نگهداشت آن را آسان می سازد.

از آنجایی که امکان تست اپلیکیشن های اندرویدی بر روی تمامی دستگاه ها (با تنظیمات و سخت افزار مختلف) وجود ندارد، توصیه می شود تست های نرم افزاری خود را بر روی دستگاه هایی با پیکربندی و سخت افزار معمول اجرا نمایید. لازم است اپلیکیشن خود را حداقل یکبار بر روی دستگاهی با پایین ترین قدرت سخت افزاری تست نمایید. هرچند بد نیست که اپلیکیشن مورد نظر را بر روی دستگاه هایی که بالاترین قدرت سخت افزاری را دارند نیز تست نمایید. برای مثال اپلیکیشن خود را بر دستگاه هایی که چگالی پیکسلی و وضوح تصویری بالایی دارند، تست کرده و مطمئن شوید که برنامه ی تحت موبایل شما با این پیکربندی نیز کاملا سازگار است.

Unit testing اپلیکیشن های اندرویدی می تواند به گروه های زیر تقسیم شود:

  • Local unit test (تست در بستر دستگاه مجازی جاوا) – تست هایی که می توان آن ها را در بستر JVM اجرا کرد در اصطلاح تست های local خوانده می شوند. تا حد امکان بهتر است از تست های local برای تست نرم افزار خود بهره بگیرید. تست های local به این علت که بر روی JVM اجرا می شوند، از لحاظ زمانی به خصوص نسبت به تست هایی که بر روی دستگاه واقعی اندروید اجرا می شوند، سریع تر به انجام می رسند.
  • Instrumented unit test(تست بر روی دستگاه واقعی اندروید) – تست هایی که بر روی یک سیستم واقعی اندروید انجام می شود. اگر می خواهید کدی را امتحان کنید که به API و توابع کتابخانه ای اندروید احتیاج دارد، در آن صورت لازم است این تست ها را بر روی دستگاه اندرویدی اجرا کنید. متاسفانه این امر سبب می شود زمان اجرای تست طولانی تر شود.
category of Android test

آموزش بخش هایی که در تست نرم افزاری، تمرکز بر روی آن قرار می گیرد

توصیه می شود در تست نرم افزار تمرکز خود را بیشتر بر روی آزمایش و کسب اطمینان از عملکرد صحیح منطق (logic) اپلیکیشن قرار دهید.

توصیه می شود تست اپلیکیشن خود را بر اساس قاعده ی زیر طراحی نمایید:

  • 1. 70-80 % از کل تست را به unit test اختصاص دهید تا بدین وسیله stability و ثبات code base تضمین شود.
  • 2. 20-30 % از تست را به صورت functional طراحی نموده و از کارکرد صحیح کد اطمینان کامل حاصل نمایید.
  • 3. و در صورتی که اپلیکیشن شما با کامپوننت هایی از سایر اپلیکیشن ها زیاد تعامل دارد، بخشی از کل تست را به صورت cross functional تعبیه نمایید.
بخش های تست نرم افزاری ندروید

در تست اپلیکیشن باید به این امر نیز توجه داشته باشید که آیا تنها خود اپلیکیشن مورد آزمایش قرار می گیرد یا رابطه ی (integration) اپلیکیشن با اپلیکیشن های دیگر نیز مورد بررسی قرار گرفته و آزمایش می شود. در صورتی که تنها خود اپلیکیشن مورد آزمایش است، می توانید از فریم ورک های اجرای تست استفاده نمایید که تنها به دانش جزئی از اپلیکیشن ها برای مثال view ID ها احتیاج است.

آموزش پیش شرط های تست گیری (testing preconditions)

بد نیست در تست اپلیکیشن های اندرویدی خود متدی به نام testPreconditions() داشته باشید که پیش شرط تمامی تست ها را بررسی می کند. چنانچه این متد در انجام وظیفه ی خود موفق نباشد، بلافاصله پی می برید که فرض یا پیش شرط های دیگر تست ها نیز نقض شده است.

آموزش ATSL و تست اپلیکیشن با ابزار JUnit 4

Android Testing Support Library (ATSL) به شما اجازه می دهد تا اپلیکیشن خود را با یک test runner سازگار با JUnit 4 مورد آزمایش قرار دهید. می توانید unit test های اپلیکیشن خود را بر روی JVM و به صورت محلی اجرا نمایید یا آن را در بستر دستگاه واقعی اندروید (Android runtime) راه اندازی کنید. علاوه بر آن، Google یک فریم ورک اجرای تست بر روی UI به نام Espresso تعبیه نموده که به شما امکان توسعه ی تست های رابط کاربری را می دهد.

برای فراهم آوردن امکان تست نرم افزار در محیط دستگاه مجازی جاوا (JVM)، افزونه ی Gradle یک نسخه ی خاص از فایل android.jar را ارائه می نماید (این فایل تحت عنوان Android mockable jar نیز شناخته می شود). فایل نام برده در اختیار کل unit test قرار گرفته تا تمامی فیلدها، متدها و کلاس ها در دسترس و قابل استفاده باشند. لازم به ذکر است که فراخوانی هر یک از توابع mockable JAR، به صورت پیش فرض، منجر به صدور خطا (exception) می شود.

بنابراین اگر کلاس های شما توابع کتابخانه ای اندروید (Android API) را فراخوانی نمی کنند، شما می توانید از فریم ورک تست گیری JUnit (یا هر فریم ورک دیگری که برای تست اپلیکیشن های جاوا ارائه شده) بدون محدودیت استفاده نمایید. چنانچه به توابع کتابخانه ای اندروید (android API) وابستگی (dependency) دارید (مجبورید از کتابخانه ها و کلاس های Android API استفاده نمایید)، در آن صورت این وابستگی ها در کد شما بایستی (برای unit test ها) از طریق فریم ورک های mocking (فریم ورک های ساختگی همچون Mockito) جایگزین و جبران گردد. برای مشاهده ی اطلاعات بیشتر جهت فعال سازی مقادیر بازگشتی برای متدهای ساختگی و شبیه سازی شده از فایل mockable.jar اندروید، به این آدرس مراجعه نمایید: Activating default return values for mocked methods in android.jar.

Android Testing Support Library این قابلیت را در اختیار توسعه دهنده قرار می دهد تا تست های نرم افزاری برای اپلیکیشن های خود طراحی کند. کتابخانه ی مذکور AndroidJUnitRunner، فریم ورک تست گیری Espresso و UI Automator را شامل می شود.

AndroidJUnitRunner امکان ساخت و اجرای تست های JUnit 4 را فراهم می کند، در حالی که چارچوب تست گیری Espresso برای طراحی تست ویژه ی لایه ی UI نرم افزار مناسب می باشد. با استفاده از UI Automator نیز برنامه نویس قادر خواهد بود تست های cross functional برای اپلیکیشن اندرویدی طراحی نماید.

AndroidJunitRunner توابع کتابخانه ای لازم برای اجرای تست نرم افزاری بر روی دستگاه حقیقی اندروید (Instrumentation API) را در قالب کلاس InstrumentationRegistry در اختیار توسعه دهنده قرار می دهد.

  • InstrumentationRegistry.getInstrumentation() تابعی است که در خروجی instrumentation فعلی و در حال اجرا را برمی گرداند.
  • InstrumentationRegistry.getContext() تابعی است که در خروجی Context پکیج instrumentation جاری را بازگردانی می نماید.
  • InstrumentationRegistery.getTargetContext() متدی است که Context اپلیکیشن مقصد را بازگردانی می نماید.
  • InstrumentationRegistry.getArguments()، یک کپی از مجموعه یا Bundle آرگومان هایی که به این instrumentation ارسال شده را در خروجی بازمی گرداند. این تابع زمانی به کار شما می آید که بخواهید به آرگومان های command line ارسال شده به instrumentation برای تست خود استفاده نمایید.

برای دسترسی و مدیریت lifecycle نیز می توانید از کلاس ActivityLifecycleMonitorRegistry استفاده نمایید.

آموزش ساختار پروژه ی اندرویدی و ایجاد پوشه ی تست

آموزش سازماندهی پروژه ی اندرویدی برای تست

برای سازمان دهی پروژه های تست اپلیکیشن، توسعه دهندگان معمولا از قرار داد خاصی پیروی می کنند. جهت سازمان دهی کد اپلکیشن، در این پروژه های اندرویدی توصیه می شود که از ساختار عنوان شده در زیر استفاده نمایید. این ساختار را ویزارد پروژه ی اندروید نیز به صورت جداگانه ارائه می دهد.

اگر از این قرارداد پیروی کنید، بی شک سیستم کامپایل اندروید (Android build system) قادر خواهد بود تست شما را به صورت خودکار بر روی دستگاه مورد نظر (JVM یا دستگاه واقعی اندروید) اجرا کند.

آموزش برطرف کردن خطای "error duplicate files in path"

در صورت برخورد با این خطا:"error duplicate files in path. Path in archive: LICENSE.txt" ، می توانید با افزودن قطعه کد زیر به فایل app/gradle.build ، به راحتی خطای مزبور را برطرف نمایید.

android {
    packagingOptions {
    exclude 'LICENSE.txt'
    }
}

آموزش اجرای Unit test بر روی JVM

آموزش اجرای Unit test بر روی نرم افزار در بستر Android runtime

Android برای تست هایی که (به صورت local) در محیط JVM یا در بستر دستگاه واقعی اندروید (Android runtime) اجرا می شوند، واژه ی unit tests را بکار می برد.

Unit test عبارت است از تستی که عملکرد و قابلیت یک کامپوننت نرم افزاری را به صورت جداگانه مورد آزمایش قرار می دهد.

اپلیکیشنی را در نظر بگیرید که در آن دکمه ای داخل یک activity، سبب فعال سازی و اجرای activity دیگری می شود. unit test ای که برای اپلیکیشن نوشته می شود، بررسی می کند آیا intent مورد نیاز (برای راه اندازی activity دوم) صادر می شود یا خیر. Unit test کاری با اینکه آیا activity دوم در نتیجه ی فراخوانده شدن intent اجرا می شود یا خیر، ندارد.

Unit test ها بر اساس ورژن ویرایش شده از کتابخانه ی اندروید android.jar اجرا می شوند. در این ویرایش تمامی modifier های final حذف شده اند. این امر به شما امکان می دهد تا از کتابخانه های شبیه سازی و ایجاد آبجکت های ساختگی (mocking library) همچون Mockito بهره بگیرید.

آموزش محل جایگذاری unit test ها در پروژه ی اندرویدی

همان طور که قبلا ذکر شد، unit test های پروژه ی اندروید باید در پوشه ی app/src/test قرار داده شوند.

آموزش کتابخانه ها/dependency های الزامی در فایل Gradle build

برای استفاده و اجرای تست های JUnit بر روی کامپوننت های نرم افزاری مربوط به اپلیکیشن خود، بایستی آن ها را در قالب dependency (کتابخانه) به فایل Gradle build خود اضافه نمایید.

dependencies {
    // Unit testing dependencies
    testCompile 'junit:junit:4.12'
    // Set this dependency if you want to use the Hamcrest matcher library
    testCompile 'org.hamcrest:hamcrest-library:1.3'
    // more stuff, e.g., Mockito
}

آموزش اجرای unit test ها از طریق سیستم کامپایل Gradle

Unit test های خود را با فراخوانی دستور gradlew test اجرا نمایید.

آموزش اجرای unit test ها از محیط کاری Android Studio

به منظور اجرای یک unit test، بر روی کلاس تست گیری (test class) در پنجره ی Project راست کلیک کرده و سپس گزینه ی Run را انتخاب نمایید.

اجرای unit test در android

آموزش محل قرارگیری نتایج و گزارش های مربوط به تست (test reports)

گزارش ها و نتایج مربوط به تست در پوشه ی app/build/reports/tests/debug/`directory ایجاد می شوند. فایل `index.html اطلاعاتی درباره ی هر یک از صفحات تست فراهم آورده و به آن ها جهت مشاهده ی اطلاعات بیشتر لینک می دهد.

آموزش فعال سازی مقادیر بازگشتی پیش فرض متدهای ساختگی (mocked methods) در فایل android.jar

به طور پیش فرض، فراخوانی هر یک از توابع موجود در فایل android.jar سبب رخداد خطا (exception) می شود. این وضعیت پیش فرض باعث می شود که unit test ها تنها کد شما را تست کنند و به هیچ یک از رفتار خاص محیط یا بستر اجرای اندروید (Android platform) وابسته نباشند. در صورت نیاز به تنظیم اختصاصی یک قابلیت یا رفتار خاص، کافی است از فریم ورک های شبیه ساز (mocking framework) جهت جایگزینی این متدها و فراخوانی آن ها استفاده نمایید.

همچنین می توانید به سیستم کامپایل Gradle دستور بدید که به عنوان خروجی متدهای فراخوانی شده، مقادیر پیش فرض مورد نظر را بازگردانی نمایند. برای این منظور کافی است تنظیمات زیر را در فایل Gradle build اپلیکیشن خود لحاظ نمایید.

android {
  // ...
  testOptions {
    unitTests.returnDefaultValues = true
  }
}
آموزش تمرین: طراحی unit test جهت تست اپلیکیشن

آموزش مرحله ی آماده سازی: ساخت پروژه ی اندروید

پروژه ی اندرویدی خود را بر اساس برنامه ای که در مباحث قبلی ساختید (Android temperature converter)، طراحی نمایید.

آموزش ساخت unit test

هدف از انجام این تمرین

طی تمرین حاضر قادر خواهید بود یک تست JUnit4 برای پروژه ی اندرویدی خود بنویسید.

آموزش افزودن dependency / کتابخانه ی JUnit

Dependency یا کتابخانه ی Junit را در فایل app/build.gradle جایگذاری نمایید. اگر پوشه ی test در پروژه ی شما موجود نبود، در آن صورت بایستی طبق فرایندی که قبلا توضیح داده شد (نحوه ی ساخت پوشه ی تست در محیط کاری Android Studio)) را طی نموده و پوشه ی مورد نیاز را ایجاد نمایید.

                        dependencies {
        // Unit testing dependencies
        testCompile 'junit:junit:4.12'
}

آموزش ساخت و طراحی تست

در پوشه ی app/src/test، دو متد تست گیری (test method) زیر را داخل کلاس ConverterUtil تعریف نمایید.

package com.vogella.android.temperature.test;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.vogella.android.temperature.ConverterUtil;
public class ConverterUtilTest {
        @Test
        public void testConvertFahrenheitToCelsius() {
                float actual = ConverterUtil.convertCelsiusToFahrenheit(100);
                // expected value is 212
                float expected = 212;
                // use this method because float is not precise
                assertEquals("Conversion from celsius to fahrenheit failed", expected,
                                actual, 0.001);
        }
        @Test
        public void testConvertCelsiusToFahrenheit() {
                float actual = ConverterUtil.convertFahrenheitToCelsius(212);
                // expected value is 100
                float expected = 100;
                // use this method because float is not precise
                assertEquals("Conversion from celsius to fahrenheit failed", expected,
                                actual, 0.001);
        }
}

آموزش اجرای unit test بر روی پروژه

بد نیست با تست unit test هایی که برای بخش های مختلف پروژه ی خود نوشته اید، اطمینان حاصل نمایید که تست ها به درستی پیاده سازی شده اند. تست های شما بایستی طبق انتظار به درستی اجرا شوند.

طراحی instrumentation test برای اجرای تست بر روی اپلیکیشن در بستر دستگاه حقیقی اندروید

آموزش Instrumentation test

API یا توابع کتابخانه ای تست گیری اندروید hook هایی را برای وصل شدن به کامپوننت های نرم افزاری و lifecycle اپلیکیشن اندروید فراهم می آورند (hook مکان یا interface ای است که در قالب کد پکیج شده ارائه شده و به برنامه نویس اجازه می دهد تا کدهایی با تنظیمات اختصاصی و دلخواه وارد متن برنامه کند. به طور مثال، توسعه دهنده می تواند کدی وارد برنامه کند که وظیفه ی آن بررسی این است که هر چند وقت یکبار یک مسیر منطقی داخل اپلیکیشن مورد نظر طی می شود).

این hook ها در اصطلاح instrumentation API خوانده شده و به تست های شما این اجازه را می دهد تا اداره ی lifecycle و event های مرتبط با تعامل کاربر را بدست بگیرند.

در شرایط معمولی، اپلیکیشن نمی تواند event های مربوط به lifecycle را کنترل کند و در این برهه کاربر مدیریت جریان یا روند اپلیکیشن را بدست می گیرد. برای مثال، زمانی که سیستم اندروید کامپوننت activity اپلیکیشن را اجرا می کند، در واقع متد onCreate() فراخوانده می شود. یا هنگامی که کاربر دکمه ای را در نمایشگر فشار می دهد، کد مربوطه از اپلیکیشن فراخوانده و اجرا می شود. توسعه دهنده می تواند با بهره گیری از توابع کتابخانه ای Instrumentation، رخدادهایی از این دست را با اجرای کد آزمایشی (test code) خود اداره کند. سپس می تواند متد finish() را صدا زده، activity را مجددا اجرا کند و بدین وسیله مطمئن شود که اطلاعات مربوط به وضعیت (instance state) activity مزبور پس از راه اندازی مجدد بازگردانی می شوند یا خیر.

Unit test هایی که زیرمجموعه ی instrumentation هستند، همان طور که پیش تر نیز ذکر شد، بجای JVM (دستگاه مجازی جاوا)، بر روی دستگاه های حقیقی اندروید یا در بستر نرم افزار شبیه ساز (emulator) اجرا می شوند. این تست ها به دستگاه واقعی اندروید و منابع آن دسترسی داشته و برای اجرای تست بر روی کامپوننت های مجزای نرم افزار و انجام unit test بسیار ساده می باشند. همان طور که می دانید، تست قابلیت های اپلیکیشن به واسطه ی unit test با بهره گیری از فریم ورک های شبیه سازی (mocking frameworks) به راحتی امکان پذیر نمی باشد. به عنوان مثال می توان به تستی اشاره کرد که کد و پیاده سازی یک Parcelable را آزمایش کرده و سعی می کند از عملکرد صحیح آن اطمینان حاصل نماید.

کلاس تست گیری که مبتنی بر instrumentation می باشد، این امکان را به شما می دهد تا event های اصلی (event های مربوط به نمایشگر/touch event) را به اپلیکیشن مورد تست ارسال نمایید

با وجود فریم ورک های تست گیری همچون Espresso که قابلیت آزمایش UI را فراهم می آورند، توسعه دهنده به ندرت مجبور می شود تا مستقیما از instrumentation API استفاده نماید.

آموزش سیستم اندروید چگونه تست ها را اجرا می کند

Test runner (کلاس تست گیری) پایه برای اجرای تست های نرم افزاری بر روی اپلیکیشن های اندرویدی، InstrumentationTestRunner است. این کلاس تمامی متدهای تست گیری را اجرا کرده و در حافظه بارگذاری می نماید. سپس به واسطه ی instrumentation API با سیستم اندروید ارتباط برقرار می کند. زمانی که تستی را جهت آزمایش اپلیکیشن اندرویدی خود اجرا می کنید، سیستم اندروید تمامی فرایندهای مربوط اپلیکیشن مورد آزمایش (که در حال تست شدن هستند) را از حافظه پاک کرده و متعاقبا یک نمونه (instance) جدید از آن را در حافظه بارگذاری می کند. لازم به توضیح است که کلاس مزبور اپلیکیشن را راه اندازی نمی کند، بلکه این امر، وظیفه ی متدهای تست گیری می باشد. به عبارت دیگر این متد تست گیری است که اداره ی lifecycle کامپوننت های نرم افزاری را بدست می گیرد.

کلاس تست گیری مورد نظر، به هنگام راه اندازی و مقداردهی اولیه (initialization)، همچنین متد onCreate() مربوط به اپلیکیشن و activity مورد تست را صدا می زند.

آموزش شبیه سازی رفتار آبجکت ها در اندروید (ایجاد آبجکت های ساختگی جهت تست)

توسعه دهنده برای اجرای تست های instrumentation بر روی نرم افزار اندرویدی خود، همچنین می تواند از فریم ورک شبیه سازی (mocking framework) Mockito استفاده نماید. با بهره گیری از این فریم ورک توسعه قادر خواهد بود آن بخش هایی از سیستم اندروید را که تست آن ها ضرورتی ندارد، با بخش هایی از این فریم ورک جایگزین نماید. ویرایش های قبلی چارچوب نرم افزاری اندروید (Android framework) کلاس های شبیه سازی /mocking classes را ویژه ی تست بخش های اپلیکیشن ارائه می دادند. اما امروزه با وجود Mockito این کلاس ها دیگر کاربردی ندارند.

آموزش محل قرارگیری تست های

همان طور که قبلا توضیح داده شد (در بخش سازماندهی پروژه و تست ها)، unit test ها بایستی در پوشه ی app/src/androidTest/java قرار گیرد.

آموزش تعریف dependency ها و testInstrumentationRunner داخل فایلGradle build

جهت استفاده از JUnit برای اپلیکیشن های اندرویدی خود، می بایست dependency (کتابخانه ی مورد نیاز) را به فایل Gradle build اضافه نمایید. علاوه بر آن لازم است android.support.test.runner.AndroidJUnitRunner را نیز در قالب کلاس testInstrumentationRunner داخل فایل build تعریف نمایید.

defaultConfig {
..... more stuff
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
dependencies {
    // Unit testing dependencies
    androidTestCompile 'junit:junit:4.12'
    // Set this dependency if you want to use the Hamcrest matcher library
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    // more stuff, e.g., Mockito
}

آموزش استفاده از @RunWith(AndroidJUnit4.class)

توصیه می شود تست را با دستور @RunWith(AndroidJUnit4.class) نیز نشانه گذاری (annotate) نمایید. AndroidJUnit4 از JUnit4 ارث بری می کند (extend می کند). بنابراین اگر از syntax/ساختار نگارشی خالص junit4 و ActivityTestRule استفاده نمایید، دیگر لزومی ندارد تست را با annotation فوق نشانه گذاری نمایید. اما در صورتی که لازم باشد تست های فریم ورک Espresso را همراه با ActivityTestRule و JUnit4 اجرا نمایید، استفاده از آن الزامی خواهد بود.

آموزش اجرای unit test از طریق سیستم کامپایل Gradle

Unit test را با فراخوانی دستور gradlew connectedCheck اجرا نمایید.

آموزش اجرای unit test از داخل محیط برنامه نویسی Android Studio

بر روی کلاس تست گیری در پنجره ی Project راست کلیک کرده و سپس گزینه ی Run را انتخاب نمایید.

آموزش محل جایگذاری گزارش های تست

گزارش های تست در پوشه ی app/build/reports/androidTests/connected/ جایگذاری می شود.

فایل index.html اطلاعات مختصری درباره ی تست ارائه داده و به تمامی صفحات آن لینک می دهند.

نحوه ی جایگزین کردن بخش های اپلیکیشن با instrumentation test ها

می توانید برای اجرای تست های مبتنی بر instrumentation و جایگزین کردن کلاس اپلیکیشن (با تست های instrumentation)، کلاس AndroidJUnitRunner و newApplication را بازنویسی (override) نمایید.

package com.vogella.android.daggerjunitmockito;
import android.app.Application;
public class MyMockApplication extends Application {
    @Override
    public void onCreate() {
       // do something important for your tests here
    }
}

آموزش Test runner:

package com.vogella.android.daggerjunitmockito;
import android.app.Application;
import android.content.Context;
import android.support.test.runner.AndroidJUnitRunner;
public class MockTestRunner extends AndroidJUnitRunner {
  @Override
  public Application newApplication(ClassLoader cl, String className, Context context)
      throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return super.newApplication(cl, MyMockApplication.class.getName(), context);
  }
}

همچنین لازم است این test runner را در فایل build.gradle ثبت (تعریف) نمایید.

android {
    /// more
        testInstrumentationRunner "com.vogella.android.daggerjunitmockito.MockTestRunner"
    }
    /// more
}
تمرین: شبیه سازی و کسب اطمینان از عملکرد صحیح قابلیت دسترسی به فایل (Mocking file access)

هدف از این تمرین

لازم نیست این کلاس را در اپلیکیشن اندرویدی خود بکار ببرید و در اینجا تنها به منظور نمایش نحوه ی استفاده از Mockito در unit test بکار برده شده است.

آموزش ایجاد کلاسی جهت تست گیری

در اپلیکیشن اندرویدی آماده ی خود و یا پروژه ای با پکیج com.vogella.android.testing.mockitocontextmock، کلاس زیر را پیاده سازی نمایید.

public class Util {
        public static void writeConfiguration(Context ctx ) {
                try (FileOutputStream openFileOutput =
                         ctx.openFileOutput( "config.txt", Context.MODE_PRIVATE);) {
                        openFileOutput.write("This is a test1.".getBytes());
                        openFileOutput.write("This is a test2.".getBytes());
                } catch (Exception e) {
                        // not handled
                }
        }
}

آموزش ساخت unit test جدید

با استفاده از فریم ورک شبیه سازی (mocking framework) Mockito، یک unit test جدید بنویسید که موارد زیر را بررسی می کند:

  • 1. openFileOutput دقیقا یکبار فراخوانی شود.
  • 2. متد write() حداقل دوبار صدا زده شود.
package com.vogella.android.testing.mockitocontextmock;
import android.content.Context;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.FileOutputStream;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class TextContextOutputStream {
@Mock
Context context;
@Mock
FileOutputStream fileOutputStream;
@Before
public void init(){
    MockitoAnnotations.initMocks(this);
}
    @Test
public void writeShouldWriteTwiceToFileSystem() {
    try {
        when(context.openFileOutput(anyString(), anyInt())).thenReturn(fileOutputStream);
        Util.writeConfiguration(context);
        verify(context, times(1)).openFileOutput(anyString(), anyInt());
        verify(fileOutputStream, atLeast(2)).write(any(byte[].class));
    } catch (Exception e) {
        e.printStackTrace();
        fail();
    }
}
}

اطلاعات بیشتر در خصوص اجرای تست بر روی اپلیکیشن های اندرویدی

آموزش کلاس های assert

توابع کتابخانه ای اجرای تست (Android testing API) بر روی اپلیکیشن های اندرویدی، علاوه بر JUnit Assert، دو کلاس دیگر به نام های MoreAsserts و ViewAsserts ارائه می دهد.

آموزش Test group ها (گروه بندی تست ها)

دستورات @SmallTest، `@MediumTest و `@LargeTest`annotations امکان گروه بندی تست ها را برای توسعه دهنده فراهم می کنند. به عبارتی روشن تر این قابلیت توسعه دهنده را قادر می سازد تا تست ها را بر اساس ویژگی خاصی گروه بندی نماید. برای مثال تنها آن دسته از تست هایی را بر روی نرم افزار اجرا کند که زمان اجرای آن ها چندان طولانی نیست یا تست هایی که زمان اجرای آن ها طولانی هستند را بر روی سرویس دهنده ی continuous integration به اجرا بگذارند (Continuous integration به یک سری روش گفته می شود که طی آن سلامت نرم افزار به طور دایم تضمین می شود، به طوری که خورده تغییرات افرادِ درگیر در پروژه باعث رخداد خطا در کل پروژه نشده و به درستی مثل تکه های پازل در کنار هم قرار گیرند).

برای اینکه تنها تست های انتخابی اجرا شوند، شما می توانید کلاس InstrumentationTestRunner را به واسطه ی افزونه ی (plug-in) Gradle تنظیم نمایید. کد زیر نمونه ای از فایل build.gradle را نمایش می دهد که با دستور @SmallTests علامت گذاری شده و به همین خاطر تنها تست هایی که زمان اجرای طولانی ندارند را اجرا می کند.

android {
  //....
  defaultConfig {
  //....
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    testInstrumentationRunnerArgument "size", "small"
  }
}
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Test;
public class ExampleTest {
    @Test
    @SmallTest
    public void validateSecondActivity() {
        // Do something not so long...
    }
    @Test
    @MediumTest
    public void validateSecondActivityAgain() {
        // Do something which takes more time....
    }
}

آموزش فیلتر کردن تست

می توانید تست های نرم افرازی خود را با annotation ها نشانه گذاری کرده و از این طریق تست های مزبور را فیلتر نمایید (برای مثال شرایطی که تست در آن اجرا می شود را دقیق مشخص کنید). در واقع کلاس اجرای تست های نرم افزاری اندروید (android test runner) به شما امکان می دهد این تست ها را به وسیله ی annotation های زیر، محدود ساخته و صریحا مشخص کنید که تست با چه شرایطی بایستی اجرا شود.

Annotation های فیلتر و محدود سازی تست
Annotation
شرح کاربرد
@RequiresDevice
تعیین می نماید که تست تنها بایستی بر روی دستگاه های واقعی اندروید اجرا شود. چنانچه محیط اجرای تست شبیه ساز باشد، تست به واسطه ی این annotation اجرا نخواهد شد.
@SdkSupress
@SDKSupress(minSdkVersion=18)

دستور @FlakyTest

می توانید با استفاده از دستور @FlakyTest به سیستم اندروید اعلان نمایید که تست را در صورت عدم موفقیت، بار دیگر اجرا کند. Annotation ذکر شده، یک attribute به نام tolerance دارد که با مقدار دهی آن می توانید تعیین نمایید که تست پس از چندبار ناموفق بودن در اجرا، failed در نظر گرفته شده و دیگر تکرار نشود.

تمرین: تست lifecycle

هدف از این تمرین

در این تمرین، استفاده از فریم ورک تست گیری نرم افزار در بستر دستگاه حقیقی اندروید (instrumentation test) به صورت عملی نمایش داده شده است. البته برای چنین تستی، بهتر است از فریم ورک های سطح بالاتر همچون Espresso استفاده نمایید.

آموزش ایجاد پروژه و تست آن

یک پروژه و activity جدید به ترتیب به نام های com.vogella.android.test.simpleactivity و MainActivity تعریف نمایید. ¬¬

حال activity دیگری به نام SecondActivity تعریف نموده و به پروژه ی نام برده اضافه نمایید. این activity بایستی از یک layout استفاده نموده و حداقل یک آبجکت TextView دربرداشته باشد. id این آبجکت بایستی بر روی "resultText" تنظیم شده و text آن با "Started" مقداردهی شده باشد.

یک فیلد EditText به فایل layout کلاس MainActivity اضافه نمایید.

یک آبجکت دکمه به فایل layout ای که مورد استفاده ی کلاس MainActivity می باشد، اضافه نمایید. زمانی که دکمه کلیک می شود، دومین activity بایستی اجرا شود.

فیلد متنی EditText را با استفاده از "text" به عنوان کلید آن، داخل آبجکت intent قرار دهید. پس از آن رشته ی http://www.vogella.com را به عنوان extra (اطلاعات اضافی که بین دو activity مبادله می شود) با انتخاب "URL" به عنوان کلید، (به وسیله ی دستور intent.putExtra("URL", "http://www.vogella.com");) داخل آبجکت intent قرار دهید.

در زیر نمونه کدی از پیاده سازی MainActivity را مشاهده می کنید.

package testing.android.vogella.com.simpleactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void onClick(View view) {
        Intent intent = new Intent(this, SecondActivity.class);
        intent.putExtra("URL", "http://www.vogella.com");
        startActivity(intent);
    }
}

آموزش ساخت کلاس و پروژه ی تست

یک instrumentation test دیگر مشابه زیر پیاده سازی نمایید.

package com.vogella.android.test.simpleactivity.test;
import android.app.Activity;
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.test.ActivityInstrumentationTestCase2;
import android.test.TouchUtils;
import android.test.UiThreadTest;
import android.test.ViewAsserts;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.vogella.android.test.simpleactivity.R;
import com.vogella.android.test.simpleactivity.MainActivity;
import com.vogella.android.test.simpleactivity.SecondActivity;
public class SecondActivityFunctionalTest extends
                ActivityInstrumentationTestCase2 {
        private static final String NEW_TEXT = "new text";
        public SecondActivityFunctionalTest() {
                super(SecondActivity.class);
        }
        public void testSetText() throws Exception {
                SecondActivity activity = getActivity();
                // search for the textView
                final TextView textView = (TextView) activity
                                .findViewById(R.id.resultText);
                // set text
                getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                                textView.setText(NEW_TEXT);
                        }
                });
                getInstrumentation().waitForIdleSync();
                assertEquals("Text incorrect", NEW_TEXT, textView.getText().toString());
        }
        @UiThreadTest
        public void testSetTextWithAnnotation() throws Exception {
                SecondActivity activity = getActivity();
                // search for the textView
                final TextView textView = (TextView) activity
                                .findViewById(R.id.resultText);
                textView.setText(NEW_TEXT);
                assertEquals("Text incorrect", NEW_TEXT, textView.getText().toString());
        }
}

آموزش استفاده از ابزار Monkey جهت ایجاد و ارسال شبه event به دستگاه

توضیحی درباره ی ابزار monkey

Monkey یک ابزار command-line است که بر روی محیط شبیه ساز یا دستگاه حقیقی اندروید اجرا شده و این قابلیت را به شما می دهد تا علاوه بر مجموعه ای از event هایی که توسط کاربر فرخوانی می شوند، تعدادی از event هایی که در سطح سیستم رخ می دهند را نیز به صورت شبه تصادفی ایجاد و به سیستم ارسال نمایید. در واقع شما با این ابزار قادر خواهید بود اپلیکیشن های در حال توسعه ی خود را به صورت تصادفی اما تکرار پذیر، تحت شرایط سنگین آزمایش نموده و مطمئن شوید که اپلیکیشن مورد نظر در صورت وجود بار کاری زیاد نیز دارای قابلیت دسترسی و مدیریت خطای بالایی می باشد (stress-test).

شما می توانید ابزار مزبور را طوری تنظیم کنید که تنها برای پکیج خاصی اجرا شده و صرفا اپلیکیشن مد نظر را تست کند.

آموزش نحوه ی استفاده از ابزار Monkey

دستور زیر 2000 رخداد تصادفی به اپلیکیشنی که اسم پکیج de.vogella.android.test.target را در بالای فایل خود دارند، ارسال می کند.

adb shell monkey -p de.vogella.android.test.target -v 2000

لازم به ذکر است که Monkey در برخی شرایط مشکلاتی را برای سرویس دهنده ی adb ایجاد می کند. در چنین موقعیتی می توانید با اجرای دو دستور زیر، سرویس دهنده ی adb را مجددا راه اندازی نموده و مشکل را برطرف کنید.

adb kill-server
adb start-server

برای اینکه مطمئن شوید توالی یا مجموعه event های تولید شده همیشه یکسان می باشد، باید پارامتر -s [seed] را به دستور اضافه نمایید.

آموزش تست آبجکت Application

در اندروید، منطق اپلیکیشن، داده ها و تنظیمات مربوط به کل برنامه داخل آبجکتی به نام application جای گرفته است. لازم است این آبجکت را تست کرده و از عملکرد صحیح آن اطمینان حاصل نمایید.

می توانید یک تست JUnit 4 برای آزمایش عملکرد اپلیکیشن نوشته و آن را بر روی JVM اجرا نمایید. در این سناریو بایستی تمامی dependency و کتابخانه ی های مربوط به آبجکت اپلیکیشن را شبیه سازی (mock) نمایید.

برای تست آبجکت application در runtime اندروید، می بایست از کلاس ApplicationTestCase استفاده نمایید. در آینده انتظار می رود که شرکت گوگل یک rule جدید برای تست آبجکت یاد شده، در کتابخانه ی JUnit4 تعبیه نماید، اما در زمان حاضر چنین امکانی برای استفاده ی توسعه دهنده فراهم نیست.

/ test runner کلاس اجرای تست محیط Android (InstrumentationTestRunner) در مرحله ی مقداردهی اولیه، به صورت خودکار یک نمونه از کلاس application می سازد. بنابراین در صورت استفاده از پردازش ناهمزمان در بدنه ی متد onCreate()، لازم است حتما این نکته را به خاطر داشته باشید.

تمرین: تست اپلیکیشن

آموزش ایجاد پروژه

یک اپلیکیشن جدید اندروید با اسم پکیج com.vogella.android.testing.applicationtest بر اساس قالب آماده ی (template) Empty Activity ایجاد نمایید.

آبجکت application را با پیاده سازی زیر به برنامه ی خود اضافه نمایید.

package com.vogella.android.testing.applicationtest;
import android.app.Application;
import java.util.ArrayList;
import java.util.List;
public class MyApplication extends Application {
    public static final List list = new ArrayList();
}

لازم است اپلیکیشن را در لایه ی XML، داخل فایل manifest نیز اعلان نمایید.








            
        
    

آموزش تعریف unit test برای تست آبجکت application

داخل پوشه ی app/src/test یک unit test جدید تعریف نمایید. تست را طوری بنویسید که انتظار داشته باشد (assert) فیلد MyApplication.list تهی نبوده و اندازه ی (size) اولیه ی آن 0 باشد.

آموزش طراحی instrumentation test برای آبجکت application

یک unit test جدید بر اساس فریم ورک تست گیری JUnit 3 به صورت زیر تعبیه نمایید.

package com.vogella.android.testing.applicationtest;
import android.content.pm.PackageInfo;
import android.test.ApplicationTestCase;
import android.test.MoreAsserts;
public class ApplicationTest extends ApplicationTestCase {
    private MyApplication application;
    public ApplicationTest() {
        super(MyApplication.class);
    }
    protected void setUp() throws Exception {
        super.setUp();
        createApplication();
        application = getApplication();
    }
    public void testCorrectVersion() throws Exception {
        PackageInfo info = application.getPackageManager().getPackageInfo(application.getPackageName(), 0);
        assertNotNull(info);
        MoreAsserts.assertMatchesRegex("\\d\\.\\d", info.versionName);
    }
}

آموزش تست سایر کامپوننت های نرم افزاری اندروید

آموزش تست سرویس

جهت تست یک سرویس، بایستی از کلاس ServiceTestRule که از Testing Support Library اندروید مشتق می شود، استفاده نمایید. در ویرایش های قبلی به منظور آزمایش کامپوننت سرویس از ServiceTestCase استفاده می شد که با ارائه ی کلاس نام برده، استفاده از آن منسوخ شد.

این rule یک مکانیزم ساده در اختیار توسعه دهنده قرار می دهد که سرویس را قبل از اجرای تست راه اندازی نموده و با اتمام آن، کامپوننت مورد نظر را متوقف می سازد. در واقع این rule تضمین می کند که سرویس به هنگام اجرا به طور کامل متصل شده باشد. می توانید سرویس را به واسطه ی یکی از توابع کمکی (helper method) فراخوانی نمایید. زمانی که تست پایان می یابد، سرویس به صورت خودکار متوقف شده و به همراه آن تمامی متدهایی که با @After نشانه گذاری شده اند نیز خاتمه می یابند.

نکته :

این rule همراه با IntentService قابل استفاده نمی باشد چرا که به مجرد اتمام اجرای متد onHandleIntent، به طور خودکار از حافظه پاک می گردد.

کد زیر یک سرویس را تست می کند.

@RunWith(AndroidJUnit4.class)
@MediumTest
public class MyServiceTest {
    @Rule
    public final ServiceTestRule mServiceRule = new ServiceTestRule();
    // test for a service which is started with startService
    @Test
    public void testWithStartedService() {
        mServiceRule.
        startService(new Intent(InstrumentationRegistry.getTargetContext(),
                        MyService.class));
        // test code
    }
    @Test
 // test for a service which is started with bindService
    public void testWithBoundService() {
        IBinder binder = mServiceRule.
                        bindService(new Intent(InstrumentationRegistry.getTargetContext(),
                                        MyService.class));
        MyService service = ((MyService.LocalBinder) binder).getService();
        assertTrue("True wasn't returned", service.doSomethingToReturnTrue());
    }
}
adb kill-server
adb start-server

آموزش تست کامپوننت نرم افزاری Content provider

به منظور تست content provider، توسعه دهنده می تواند از کلاس ProviderTestCase2 استفاده کند. این کلاس به صورت خودکار از کامپوننت (content provider) مورد تست، نمونه سازی کرده و آبجکت IsolatedContext را در آن درج می نماید. گرچه این context یا بستر از سیستم اندروید مجزا است، اما همچنان اجازه ی دسترسی به فایل و دیتابیس را فراهم می کند. در واقع استفاده از آبجکت IsolatedContext این اطمینان را به وجود می آورد که provider مورد تست، تاثیری بر روی دستگاه واقعی اندروید نمی گذارد.

کلاس نام برده همچنین با ارائه ی متدی به نام getMockContentResolver() امکان دسترسی به کلاس MockContentResolver را فراهم می سازد.

شما بایستی تمامی عملیات مربوط به provider را آزموده و همچنین بررسی نمایید که در صورت فراخوانی provider با URI یا projection غیرمجاز چه اتفاقی رخ می دهد.

آموزش تست Loader

جهت تست کارکرد یک loader، توسعه دهنده می بایست از کلاس `LoaderTestCase` استفاده نماید. البته در آینده ی نزدیک انتظار می رود که شرکت گوگل با معرفی یک rule جدید در کتابخانه ی JUnit4، کلاس نام برده را منسوخ نماید.

آموزش Annotation های متفرقه یا پشتیبان

علاوه بر annotation های نام برده، شرکت گوگل یک پکیج حاوی annotation های جدید در اختیار توسعه دهنده قرار می دهد. پکیج مزبور تعدادی annotation metadata (دستوراتی که اطلاعاتی را درباره ی عملکرد کد شرح می دهند) ارائه می دهد که با استفاده از آن ها در متن برنامه، توسعه دهنده قادر است از رخداد خطا در عملکرد اپلیکیشن جلوگیری نماید.

جهت استفاده از این annotation ها، کافی است کتابخانه ی (dependency) زیر را به فایل Gradle build خود اضافه نمایید.

dependencies {
    compile 'com.android.support:support-annotations:22.2.0'
}

جهت دریافت اطلاعات بیشتر درباره ی annotation ها می توانید به آدرس http://tools.android.com/tech-docs/support-annotations مراجعه نمایید.

آموزش Annotation های مربوط به Nullness

با درج دستور @Nullable در بالای کد توسعه دهنده به سیستم اعلان می کند که یک پارامتر یا مقدار بازگشتی می تواند null باشد. به طور مشابه، دستور @NonNull در بالای کد مربوطه نشانگر این است که یک پارامتر (یا مقدار بازگشتی) نمی تواند و نباید null باشد.

آموزش Annotation های مربوطه به thread

در صورتی که یک متد تنها از thread خاصی قابل فراخوانی باشد، می توانید آن را با یکی از annotation های زیر نشانه گذاری نمایید.

  • @UiThread
  • @MainThread
  • @WorkerThread
  • @BinderThread
مثال:
@WorkerThread
protected abstract Result doInBackground(Params... params);
@MainThread
  protected void onProgressUpdate(Progress... values) {
  }

آموزش تهیه ی گزارش از مقدار کد تست شده از کل پروژه (Code coverage report)

Code coverage report بیانگر این است چه میزان از کل کد اپلیکیشن شما توسط unit test مورد آزمایش قرار گرفته است. به منظور تهیه ی چنین گزارشی، می توانید تنظیمات جداگانه ای برای اجرا (launch config) آن تعبیه نمایید. برای نیل به این هدف، پکیج مورد نظر خود را انتخاب نموده و پس از باز نمودن منو، آیتم [Create Tests in…] را انتخاب نمایید.

ریپورت گیری

حال می توانید تنظیمات (config) runtime جدید برای code coverage یا میزان کد مورد آزمایش خود ایجاد نمایید. پس از اجرا، یک گزارش تهیه شده و به نمایش گذاشته می شود.

کانفیگ setting run time demo

آموزش ساخت پوشه ی تست در محیط کاری Android Studio

ویرایش های جدید محیط برنامه نویسی Android Studio، یک پوشه ی تست (test folder) به قالب آماده ی پروژه ی پیش فرض (project template) خود اضافه کرده است. در صورت استفاده از قالبی که قابلیت ایجاد پوشه ی تست را به صورت درون ساخته ندارد، بایستی این پوشه را خود به صورت دستی ایجاد نمایید. جهت ایجاد پوشه ی تست در محیط کاری مذکور، کادر Project را باز نمایید. در این view می توانید ساختار درختی و directory structure پروژه ی خود را مشاهده نمایید.

فایل تست در آندروید

Src را انتخاب نموده و پس از باز کردن Context menu یک پوشه ی تست (test folder) جدید ایجاد نمایید.

یافتن فایل تست در اندروید جایابی فایل تست در اندروید نهایی کردن جایابی فایل تست در اندروید

در صورتی که همچنان کامل انجام نشده باشد، کتابخانه ی (dependency) JUnit را به فایل Gradle build اضافه نمایید.

dependencies {
    // Unit testing dependencies
    testCompile 'junit:junit:4.12'
    // Set this dependency if you want to use the Hamcrest matcher library
    testCompile 'org.hamcrest:hamcrest-library:1.3'
    // more stuff, e.g., Mockito
}
توجه :

لازم به ذکر است که در نتیجه ی ساخت پوشه ی Java، ممکن است پوشه ی جدید تست به عنوان source file به فایل gradle.build اضافه گردد. چنانچه دستور زیر در فایل app/build.gradle موجود باشد، در آن صورت می بایست کد ذکر شده را کاملا از فایل حذف نمایید. تست نباید به هیچ وجه به عنوان یک source folder معمولی در نظر گرفته شود.

sourceSets { main { java.srcDirs = ['src/main/java', 'src/test/java/'] } }

پس از آن کافی است unit test خود را به این ساختار درختی پوشه ها یا folder structure اضافه نمایید.

1395/12/27 6468 1901
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...